#include "./ota_service.h"
#include "../pub_driver/flash.h"
#include "../cfg/ota_info.h"
#include "../utils/byte_view.h"
#include "../xlib/compat.h"
#include "../utils/md5.h"

#ifdef APP_MAIN
#include "../main_driver/cmd_uart.h"
#include "../rtx/rtx_os.h"
using namespace main_driver;
using namespace pub_driver;
#define IO  cmdUart
#else
#include "../pub_driver/cmd_can.h"
using namespace pub_driver;
#define IO  cmdCAN
#endif


namespace pub_service {

struct OtaCtx {
    uint32_t pkg_size = 0;
    uint16_t pkg_id = 0;
    uint32_t pkg_index = 0;

    void init(uint32_t pkg_size) {
        this->pkg_size = pkg_size;
        pkg_id = 0;
        pkg_index = 0;
    }

    Error write(uint16_t id, const ByteView &bw) {
        Error err;
        if (id != pkg_id) {
            err = ec::pub::OTA_ID_INVALID;
            goto finish;
        }

        err = flash::write(
            flash::OTA_DATA_ADDR + pkg_index, 
            bw.buf,
            bw.len
        );
        if (err) {
            goto finish;
        }

        pkg_index += bw.len;
        pkg_id ++;

    finish:
        return err;
    }

};

static OtaCtx otaCtx;

void ota_start(const conn::Frame &frame) {
    uint32_t pkg_size;
    Error err = frame.parse(pkg_size);
    if (err) {
        goto finish;
    }
    err = flash::erase(flash::OTA_DATA_ADDR, pkg_size);
    if (err) {
        goto finish;
    }
    otaCtx.init(pkg_size);
finish:
    IO.fast_res(frame, err);
}

void ota_translate(const conn::Frame &frame) {
    uint16_t id;
    ByteView bw;
    Error err = frame.parse(id, bw);
    if (err) {
        goto finish;
    }   
    err = otaCtx.write(id, bw);
finish:
    IO.fast_res(frame, err);
}

void ota_complete(const conn::Frame &frame) {
    ByteView md5_bw;
    Error err = frame.parse(md5_bw);
    if (err) {
        goto finish;
    }

    if (md5_bw.len != 16) {
        err = ec::pub::OTA_MD5_INVALID;
        goto finish;
    }

    if (otaCtx.pkg_index != otaCtx.pkg_size) {
        err = ec::pub::OTA_PKG_INVALID;
        goto finish;
    }

    uint8_t md5[16];
    utils::md5::sum(
        reinterpret_cast<const void *>(flash::OTA_DATA_ADDR),
        otaCtx.pkg_size,
        md5
    );

    if (memcmp(md5, md5_bw.buf, 16) != 0) {
        err = ec::pub::OTA_MD5_INVALID;
        goto finish;
    }

    cfg::otaInfo->init(otaCtx.pkg_size);
    err = cfg::otaInfo.save();

finish:
    IO.fast_res(frame, err);

    if (err.is_ok()) {
        osDelay(500);
        NVIC_SystemReset();
    }
}


}
